#============================================================
#	Random Rooms
#============================================================
# @datetime: 2022-3-13 22:42:24
#============================================================
extends Node2D


##  生成完成 
signal generated


const Scn_Rooms = preload("Rooms.tscn")


const MOVE_CHANGE := {
	Vector2.LEFT : 0.3,
	Vector2.RIGHT: 0.3,
	Vector2.DOWN : 0.1,
}


## 房间数量大小
export var grid_size : Vector2 = Vector2(4, 4)
## 最小的左右移动的距离
export var min_left_right_move : int = 1
## 关卡的地图
export var level : NodePath 
onready var _level = get_node(level) as TileMap
## 边缘空墙宽度
export var margin_width : int = 2


var tile_data : Dictionary = {}


var __rooms__ : Rooms = Scn_Rooms.instance()
# 生成随机数
var __rng__ := RandomNumberGenerator.new()
# 移动路径
var __path__ := {}
# 向左右移动次数
var __left_right_move_count__ : int = 0
# 向下移动的次数
var __down_counter__ : int = 0
# 当前移动的位置
var __current_move_pos__ : Vector2 = Vector2(0,0)
# 门瓷砖
var __room_door_cell__ := {}
# 起始位置
var __start_pos__ : Vector2 = Vector2(0,0)
# 结束位置
var __end_pos__ : Vector2 = Vector2(0,0)


#============================================================
#   Set/Get
#============================================================
##  在地图范围内
## @pos  
func is_in_map_range(pos: Vector2):
	return pos.x >= 0 and pos.x < grid_size.x

##  超出底部
## @return  
func is_out_bottom(pos: Vector2) -> bool:
	return pos.y >= grid_size.y

##  小于最小左右移动次数
## @return  
func is_loss_min_left_right_count() -> bool:
	return __left_right_move_count__ < min_left_right_move

## 房间位置在地图中的映射
func map_to_world(pos: Vector2) -> Vector2:
	return pos * __rooms__.room_size * __rooms__.cell_size

## 这个位置之上是否有房间
func has_up_room(curr_pos) -> bool:
	var up_pos = Vector2(curr_pos.x, curr_pos.y - 1)
	return __path__.has(up_pos)

## 获取位置之上的房间数据
func get_up_room(curr_pos: Vector2) -> Dictionary:
	var up_pos = Vector2(curr_pos.x, curr_pos.y - 1)
	return __path__[up_pos] as Dictionary

## 获取开始的位置
func get_start_pos() -> Vector2:
#	return map_to_world(__start_pos__)
	return __start_pos__

##  获取结束的位置 
func get_end_pos() -> Vector2:
	return map_to_world(__path__.keys()[-1])

func get_map_rect() -> Rect2:
	var rect = _level.get_used_rect()
	rect.size *= _level.cell_size
	return rect


#============================================================
#   内置
#============================================================
func _ready():
	MapData.map = get_parent()
	generate()


#============================================================
#   自定义
#============================================================
##  生成
func generate():
	Rooms.init_tile_type()
	
	# 最小左右移动大小不能超出房间x数量大小
	min_left_right_move = clamp(min_left_right_move, 0, grid_size.x)
	__down_counter__ = 0
	__rng__.randomize()
	# 开始生成
	_generate()
	_update_move_path()
	# 开始填充地图
	_place_map()
	_place_empty()
	_place_border()
	
	# 设置位置的瓷砖
	var start_pos : Vector2 = __path__.keys()[0]
	for pos in __room_door_cell__:
		var room_pos = __room_door_cell__[pos]['room_pos']
		if room_pos == start_pos:
			_level.set_cellv(pos, 3)
			__start_pos__ = _level.map_to_world(pos) + Vector2(8,8)
			Logger.info(self, ["开始门的位置 ", pos, __start_pos__], " ", true)
	
	var end_pos : Vector2 = __path__.keys()[-1]
	for pos in __room_door_cell__:
		var room_pos = __room_door_cell__[pos]['room_pos']
		if room_pos == end_pos:
			_level.set_cellv(pos, 3)
			__end_pos__ = _level.map_to_world(pos)
			Logger.info(self, ["结束门的位置 ", pos, __end_pos__], " ", true)
	
	# 更新自动图块规则
	_level.update_bitmask_region(
		_level.get_used_rect().position,
		_level.get_used_rect().end
	)
	_level.update_bitmask_area(_level.get_used_rect().position)
	_level.update_dirty_quadrants()
	
	emit_signal("generated")


# 生成
func _generate():
	var pos = Vector2(__rng__.randi_range(1, grid_size.x-2), 0)
	__current_move_pos__ = pos
	_move(MathUtil.rand_item([Vector2.LEFT, Vector2.RIGHT]))


# 开始移动路径
func _update_move_path():
	var dirs := MOVE_CHANGE.keys()
	var probab := MOVE_CHANGE.values()
	while true:
		# 根据概率值返回方向
		var dir : Vector2 = MathUtil.list_probability(
			MOVE_CHANGE.keys(), MOVE_CHANGE.values()
		)
		# 到达底部退出循环
		if is_out_bottom(__current_move_pos__ + dir):
			break
		_move(dir)


##  添加路径点
## @pos  
## @dir  
func _add_path_point(pos: Vector2, dir: Vector2):
	if not __path__.empty():
		if __path__.has(pos):
			# 在此之上有房间
			if (dir == Vector2.DOWN 
				and has_up_room(pos)
			):
				# 如果是向下是闭合的，则改为打开的
				var up_data : Dictionary = get_up_room(pos)
				if up_data['type'] in Rooms.BOTTOM_CLOSED:
					var up_up_room : Dictionary = {}
					if has_up_room(Vector2(pos.x, pos.y - 1)):
						up_up_room = get_up_room(Vector2(pos.x, pos.y - 1))
					# 如果上面的这个房间的上面也是向下的
					# 则这个房间向上通道也打开，否则随机
					var type: int = (
						MathUtil.rand_item(Rooms.BOTTOM_OPENED)
						if (not up_up_room.empty() 
							and up_up_room in Rooms.BOTTOM_OPENED)
						else Rooms.Type.LRTB
					)
					up_data['type'] = type
				else:
					up_data['type'] = Rooms.Type.LRTB
		
		else:
			# 上一个房间的数据
			var last : Dictionary = __path__.values()[-1]
			if (last.type in Rooms.BOTTOM_CLOSED 
				and dir == Vector2.DOWN
			):
				var type: int = (
					MathUtil.rand_item(Rooms.BOTTOM_OPENED)
					if __down_counter__ < 2
					else Rooms.Type.LRTB
				)
				last.type = type
	
	var type: int = (
		Rooms.Type.LRT
		if dir == Vector2.DOWN
		else __rng__.randi_range(1, Rooms.Type.size() - 1)
	)
	if (not __path__.has(pos) 
		or __path__[pos] in Rooms.BOTTOM_CLOSED
	):
		__path__[pos] = {
			"offset": dir,
			"type": type,
		}


# 移动到下一个方向
func _move(dir: Vector2):
	var next_pos = __current_move_pos__ + dir
	if not is_in_map_range(next_pos):
		return
	
	if dir == Vector2.DOWN:
		__left_right_move_count__ = 0
		__down_counter__ += 1
	else:
		__down_counter__ = 0
	__left_right_move_count__ += 1
	__current_move_pos__ = next_pos
	_add_path_point(next_pos, dir)


#============================================================
#   填充内容，填充到一个地图里
#============================================================
## 复制房间图块到地图中
## @offset  偏移位置（房间位置）添加的位置会追加这个值
## @data_list  
func _copy_to_level(offset: Vector2, data_list: Array):
	var world_pos =  offset * __rooms__.room_size
	for data in data_list:
		var pos : Vector2 = data.offset + world_pos
		match data.cell:
			Rooms.TileType.Barrel:
				if randf() < 0.2:
					_level.set_cell(pos.x, pos.y, data.cell, 
						false, false, false, 
						data.coord
					)
			
			_:
				_level.set_cell(pos.x, pos.y, data.cell, 
					false, false, false, 
					data.coord if data.has("coord") else Vector2.ZERO
				)


# 填充地图内容
func _place_map():
	# 填充路径房间
	var index = 0
	for pos in __path__:
		var data = __path__[pos]
		var room_data = __rooms__.get_room_data(data.type)
		_copy_to_level(pos, room_data)


# 填充剩余空白内容
func _place_empty():
	for x in grid_size.x:
		for y in grid_size.y:
			var pos = Vector2(x, y)
			if __path__.has(pos):
				continue
			
			var data : Array
			if (pos.x in [0, grid_size.x]
				and randf() < 0.05	# 在地图边缘有几率生成封闭房间
			):
				data = __rooms__.get_room_data(Rooms.Type.SIDE)
			else:
				data = __rooms__.get_room_data(
					MathUtil.rand_item(Rooms.BOTTOM_CLOSED)
				)
			_copy_to_level(pos, data)


##  填充地图边缘
func _place_border():
	var data := []
	var map_size : Vector2 = __rooms__.room_size * grid_size
	
	var rect = _level.get_used_rect()
	
	# 填充周围的瓷砖
	var points = fill(rect, margin_width)
	for pos in points:
		data.push_back({
			"offset" : pos,
			"cell": Rooms.TileType.Background,
			"coord": Vector2(randi() % 3, 0)
		})
	
	# 上下行
	for x in range(-1, rect.size.x):
		for y in [-1, rect.size.y]:
			data.append({
				"offset" : Vector2(x, y),
				"cell": Rooms.TileType.Brick,
			})
	# 左右列
	for x in [-1, rect.size.x]:
		for y in range(-1, rect.size.y):
			data.append({
				"offset" : Vector2(x, y),
				"cell": Rooms.TileType.Brick,
			})
	
	_copy_to_level(Vector2(0,0), data)


##  填充四周的砖块
## @map_rect  
## @width  
func fill(map_rect: Rect2, width: int):
	var points = []
	var data = rect_border(map_rect, width)
	for dir in data:
		for pos in data[dir]:
			points.append(pos + map_rect.position)
	return points


func rect_border(rect: Rect2, width: int):
	var around : Dictionary = {}
	
	var top_down_size : Vector2 = Vector2(rect.size.x, width) + Vector2.LEFT.abs() * width * 2
	around[Vector2.UP] = _cell_list(top_down_size, Vector2(0, -width) + Vector2.LEFT * width)
	around[Vector2.DOWN] = _cell_list(top_down_size, Vector2(0, rect.size.y) + Vector2.LEFT * width)
	
	var left_right_size : Vector2 = Vector2(width, rect.size.y)
	around[Vector2.LEFT] = _cell_list(left_right_size, Vector2(-width, 0))
	around[Vector2.RIGHT]  = _cell_list(left_right_size, Vector2(rect.size.x, 0))
	return around


func _cell_list(size: Vector2, offset: Vector2) -> Array:
	var list = []
	for x in size.x:
		for y in size.y:
			list.append(offset + Vector2(x, y))
	return list


